// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package ssa

import (
	"cmd/compile/internal/abi"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/types"
	"cmd/internal/dwarf"
	"cmd/internal/obj"
	"cmd/internal/src"
	"encoding/hex"
	"fmt"
	"internal/buildcfg"
	"math/bits"
	"sort"
	"strings"
)

type SlotID int32
type VarID int32

// FuncDebug包含中变量的所有调试信息
// 作用变量由其LocalSlot标识，LocalSlot可能是
// 分解较大变量的结果。
type FuncDebug struct {
	// 插槽是调试信息中使用的所有插槽，由其插槽ID索引。
	Slots []LocalSlot
	// 用户变量，由VarID索引。
	Vars []*ir.Name
	// 构成每个变量的插槽，由VarID索引。
	VarSlots [][]SlotID
	// 位置列表数据，按变量索引。必须由PutLocationList处理。
	LocationLists [][]byte

	// 由用户填写。将块和值ID转换为PC。
	GetPC func(ID, ID) int64
}

type BlockDebug struct {
	// 块是否对用户变量有任何更改。
	relevant bool
	// 如果已完全处理，则在块的末尾显示状态。初始化后不可变。
	endState []liveSlot
}

// liveSlot是在区块入口/出口的loc中活动的插槽。
type liveSlot struct {
	// 一个内联的VarLoc，所以它压缩成16字节而不是20字节。
	Registers RegisterSet
	StackOffset

	slot SlotID
}

func (loc liveSlot) absent() bool {
	return loc.Registers == 0 && !loc.onStack()
}

// StackOffset编码值是否在堆栈上，如果在堆栈上，则在何处。它是
// 一个31位整数，在低位后跟一个存在标志。
type StackOffset int32

func (s StackOffset) onStack() bool {
	return s != 0
}

func (s StackOffset) stackOffsetValue() int32 {
	return int32(s) >> 1
}

// stateAtPC是某一点上所有变量的当前状态。
type stateAtPC struct {
	// 每个已知插槽的位置，由SlotID索引。
	slots []VarLoc
	// 每个寄存器中存在的插槽，按寄存器编号索引。
	registers [][]SlotID
}

// 重置使用live中的live变量填充状态。
func (state *stateAtPC) reset(live []liveSlot) {
	slots, registers := state.slots, state.registers
	for i := range slots {
		slots[i] = VarLoc{}
	}
	for i := range registers {
		registers[i] = registers[i][:0]
	}
	for _, live := range live {
		slots[live.slot] = VarLoc{live.Registers, live.StackOffset}
		if live.Registers == 0 {
			continue
		}

		mask := uint64(live.Registers)
		for {
			if mask == 0 {
				break
			}
			reg := uint8(bits.TrailingZeros64(mask))
			mask &^= 1 << reg

			registers[reg] = append(registers[reg], live.slot)
		}
	}
	state.slots, state.registers = slots, registers
}

func (s *debugState) LocString(loc VarLoc) string {
	if loc.absent() {
		return "<nil>"
	}

	var storage []string
	if loc.onStack() {
		storage = append(storage, "stack")
	}

	mask := uint64(loc.Registers)
	for {
		if mask == 0 {
			break
		}
		reg := uint8(bits.TrailingZeros64(mask))
		mask &^= 1 << reg

		storage = append(storage, s.registers[reg].String())
	}
	return strings.Join(storage, ",")
}

// VarLoc描述部分用户变量的存储。
type VarLoc struct {
	// 此变量在中可用的寄存器。可能不止一个
	// 在各种情况下，例如在寄存器之间移动。
	Registers RegisterSet

	StackOffset
}

func (loc VarLoc) absent() bool {
	return loc.Registers == 0 && !loc.onStack()
}

var BlockStart = &Value{
	ID:  -10000,
	Op:  OpInvalid,
	Aux: StringToAux("BlockStart"),
}

var BlockEnd = &Value{
	ID:  -20000,
	Op:  OpInvalid,
	Aux: StringToAux("BlockEnd"),
}

var FuncEnd = &Value{
	ID:  -30000,
	Op:  OpInvalid,
	Aux: StringToAux("FuncEnd"),
}

// RegisterSet是寄存器的位图，由Register.num索引。
type RegisterSet uint64

// 如果当前
// 函数由GOSSAFUNC标记（用于指向stdout或html的ssa输出）。
func (s *debugState) logf(msg string, args ...interface{}) {
	if s.f.PrintOrHtmlSSA {
		fmt.Printf(msg, args...)
	}
}

type debugState struct {
	// 请参阅调试。
	slots    []LocalSlot
	vars     []*ir.Name
	varSlots [][]SlotID
	lists    [][]byte

	// 每个插槽汇总到的用户变量，由SlotID索引。
	slotVars []VarID

	f              *Func
	loggingEnabled bool
	registers      []Register
	stackOffset    func(LocalSlot) int32
	ctxt           *obj.Link

	// 与每个值关联的名称（插槽），按值ID索引。
	valueNames [][]SlotID

	// 正在运行的任何分析的当前状态。
	currentState stateAtPC
	liveCount    []int
	changedVars  *sparseSet

	// 每个用户变量的挂起位置列表项，由VarID索引。
	pendingEntries []pendingEntry

	varParts           map[*ir.Name][]SlotID
	blockDebug         []BlockDebug
	pendingSlotLocs    []VarLoc
	liveSlots          []liveSlot
	liveSlotSliceBegin int
	partsByVarOffset   sort.Interface
}

func (state *debugState) initializeCache(f *Func, numVars, numSlots int) {
	// 每个块一个块调试。在allocBlock中初始化。
	if cap(state.blockDebug) < f.NumBlocks() {
		state.blockDebug = make([]BlockDebug, f.NumBlocks())
	} else {
		// 这个局部变量和下面类似的变量启用编译器
		// 优化。不要内联它们。
		b := state.blockDebug[:f.NumBlocks()]
		for i := range b {
			b[i] = BlockDebug{}
		}
	}

	// 每个值的插槽列表。重用以前的子切片。
	if cap(state.valueNames) < f.NumValues() {
		old := state.valueNames
		state.valueNames = make([][]SlotID, f.NumValues())
		copy(state.valueNames, old)
	}
	vn := state.valueNames[:f.NumValues()]
	for i := range vn {
		vn[i] = vn[i][:0]
	}

	// currentState的插槽和寄存器内容。已通过reset（）清除。
	if cap(state.currentState.slots) < numSlots {
		state.currentState.slots = make([]VarLoc, numSlots)
	} else {
		state.currentState.slots = state.currentState.slots[:numSlots]
	}
	if cap(state.currentState.registers) < len(state.registers) {
		state.currentState.registers = make([][]SlotID, len(state.registers))
	} else {
		state.currentState.registers = state.currentState.registers[:len(state.registers)]
	}

	// 被前辈多次使用。
	if cap(state.liveCount) < numSlots {
		state.liveCount = make([]int, numSlots)
	} else {
		state.liveCount = state.liveCount[:numSlots]
	}

	// 一个相对较小的切片，但多次用作processValue的返回。
	state.changedVars = newSparseSet(numVars)

	// 每个用户变量都有一个挂起的条目，带有跟踪其每个部分的空间。
	numPieces := 0
	for i := range state.varSlots {
		numPieces += len(state.varSlots[i])
	}
	if cap(state.pendingSlotLocs) < numPieces {
		state.pendingSlotLocs = make([]VarLoc, numPieces)
	} else {
		psl := state.pendingSlotLocs[:numPieces]
		for i := range psl {
			psl[i] = VarLoc{}
		}
	}
	if cap(state.pendingEntries) < numVars {
		state.pendingEntries = make([]pendingEntry, numVars)
	}
	pe := state.pendingEntries[:numVars]
	freePieceIdx := 0
	for varID, slots := range state.varSlots {
		pe[varID] = pendingEntry{
			pieces: state.pendingSlotLocs[freePieceIdx : freePieceIdx+len(slots)],
		}
		freePieceIdx += len(slots)
	}
	state.pendingEntries = pe

	if cap(state.lists) < numVars {
		state.lists = make([][]byte, numVars)
	} else {
		state.lists = state.lists[:numVars]
		for i := range state.lists {
			state.lists[i] = nil
		}
	}

	state.liveSlots = state.liveSlots[:0]
	state.liveSlotSliceBegin = 0
}

func (state *debugState) allocBlock(b *Block) *BlockDebug {
	return &state.blockDebug[b.ID]
}

func (state *debugState) appendLiveSlot(ls liveSlot) {
	state.liveSlots = append(state.liveSlots, ls)
}

func (state *debugState) getLiveSlotSlice() []liveSlot {
	s := state.liveSlots[state.liveSlotSliceBegin:]
	state.liveSlotSliceBegin = len(state.liveSlots)
	return s
}

func (s *debugState) blockEndStateString(b *BlockDebug) string {
	endState := stateAtPC{slots: make([]VarLoc, len(s.slots)), registers: make([][]SlotID, len(s.registers))}
	endState.reset(b.endState)
	return s.stateString(endState)
}

func (s *debugState) stateString(state stateAtPC) string {
	var strs []string
	for slotID, loc := range state.slots {
		if !loc.absent() {
			strs = append(strs, fmt.Sprintf("\t%v = %v\n", s.slots[slotID], s.LocString(loc)))
		}
	}

	strs = append(strs, "\n")
	for reg, slots := range state.registers {
		if len(slots) != 0 {
			var slotStrs []string
			for _, slot := range slots {
				slotStrs = append(slotStrs, s.slots[slot].String())
			}
			strs = append(strs, fmt.Sprintf("\t%v = %v\n", &s.registers[reg], slotStrs))
		}
	}

	if len(strs) == 1 {
		return "(no vars)\n"
	}
	return strings.Join(strs, "")
}

// slotCanonicalizer是一个用于查找和规范化的表
// LocalSlot的类型不敏感（例如，考虑到
// 插槽的基本名称、偏移量和宽度，但忽略插槽
// 类型）。
type slotCanonicalizer struct {
	slmap  map[slotKey]SlKeyIdx
	slkeys []LocalSlot
}

func newSlotCanonicalizer() *slotCanonicalizer {
	return &slotCanonicalizer{
		slmap:  make(map[slotKey]SlKeyIdx),
		slkeys: []LocalSlot{LocalSlot{N: nil}},
	}
}

type SlKeyIdx uint32

const noSlot = SlKeyIdx(0)

// slotKey是LocalSlot的不区分类型的封装；信息技术
// 用于在slotCanonicalizer中为贴图设置关键帧。
type slotKey struct {
	name        *ir.Name
	offset      int64
	width       int64
	splitOf     SlKeyIdx // SLTcanonicalizer中slkeys切片中的idx
	splitOffset int64
}

// 查找在插槽规范化程序“sc”中查找本地插槽，返回
// 插槽的规范索引，并在需要时将其添加到表中
// 是返回值是规范插槽索引，是一个布尔值，指示
// 是否已在表中找到插槽（TRUE=>found）。
func (sc *slotCanonicalizer) lookup(ls LocalSlot) (SlKeyIdx, bool) {
	split := noSlot
	if ls.SplitOf != nil {
		split, _ = sc.lookup(*ls.SplitOf)
	}
	k := slotKey{
		name: ls.N, offset: ls.Off, width: ls.Type.Width,
		splitOf: split, splitOffset: ls.SplitOffset,
	}
	if idx, ok := sc.slmap[k]; ok {
		return idx, true
	}
	rv := SlKeyIdx(len(sc.slkeys))
	sc.slkeys = append(sc.slkeys, ls)
	sc.slmap[k] = rv
	return rv, false
}

func (sc *slotCanonicalizer) canonSlot(idx SlKeyIdx) LocalSlot {
	return sc.slkeys[idx]
}

// PopulateABIInRegArgOps检查函数的输入块
// 并查找丢失或部分丢失的传入参数
// OpArg{Int，Float}Reg值，在
// 他们失踪的案例。例子：
// None
// func foo（s字符串，已用int，未用int）int{
// 返回透镜+已使用
// }
// None
// 在上面的函数中，传入参数“used”是完全活动的，
// “notused”不活动，“s”部分活动（仅长度
// 使用字符串的字段）。在调试值
// 分析运行时，我们可能会看到具有以下内容的入口块：
// None
// b1：
// v4=ArgIntReg<uintpttr>{s+8}[0]：BX
// v5=ArgIntReg<int>{used}[0]：CX
// None
// 虽然这是实时传入参数的准确图像，
// 我们还希望为非活动参数（或
// 他们的非活动部件），例如
// None
// b1：
// v9=ArgIntReg<*uint8>{s+0}[0]：AX
// v4=ArgIntReg<uintpttr>{s+8}[0]：BX
// v5=ArgIntReg<int>{used}[0]：CX
// v10=ArgIntReg<int>{unused}[0]：DI
// None
// 此函数用于检查live OpArg{Int，Float}Reg值和
// 为非活动参数或
// 部分带电参数的非带电部分。
// None
func PopulateABIInRegArgOps(f *Func) {
	pri := f.ABISelf.ABIAnalyzeFuncType(f.Type.FuncType())

	// 在制造与零件的拆分相对应的新插槽时
	// 组合参数时，我们希望避免创建新的子插槽
	// 仅在类型上与某些现有子插槽不同，因为
	// 调试位置分析将该插槽视为一个单独的插槽
	// 实体要实现这一点，请创建现有数据库的查找表
	// 类型为INSENTTIVE的插槽。
	sc := newSlotCanonicalizer()
	for _, sl := range f.Names {
		sc.lookup(*sl)
	}

	// 如果f.NamedValues不存在，则将插槽->值条目添加到f.NamedValues。
	addToNV := func(v *Value, sl LocalSlot) {
		values, ok := f.NamedValues[sl]
		if !ok {
			// 我还没见过这个位置。
			sla := f.localSlotAddr(sl)
			f.Names = append(f.Names, sla)
		} else {
			for _, ev := range values {
				if v == ev {
					return
				}
			}
		}
		values = append(values, v)
		f.NamedValues[sl] = values
	}

	newValues := []*Value{}

	abiRegIndexToRegister := func(reg abi.RegIndex) int8 {
		i := f.ABISelf.FloatIndexFor(reg)
		if i >= 0 { // 浮动汇率
			return f.Config.floatParamRegs[i]
		} else {
			return f.Config.intParamRegs[reg]
		}
	}

	// 帮助器构造一个新的OpArg{Float，Int}Reg op值。
	var pos src.XPos
	if len(f.Entry.Values) != 0 {
		pos = f.Entry.Values[0].Pos
	}
	synthesizeOpIntFloatArg := func(n *ir.Name, t *types.Type, reg abi.RegIndex, sl LocalSlot) *Value {
		aux := &AuxNameOffset{n, sl.Off}
		op, auxInt := ArgOpAndRegisterFor(reg, f.ABISelf)
		v := f.newValueNoBlock(op, t, pos)
		v.AuxInt = auxInt
		v.Aux = aux
		v.Args = nil
		v.Block = f.Entry
		newValues = append(newValues, v)
		addToNV(v, sl)
		f.setHome(v, &f.Config.registers[abiRegIndexToRegister(reg)])
		return v
	}

	// 穿过入口区寻找
	// OpArg{Int，Float}Reg操作。记录他们在表格中使用的插槽
	// （“sc”）。我们对slot表使用不区分类型的查找，
	// 因为我们从ABI分析器得到的类型并不总是匹配的
	// 编译器在创建OpArg{Int，Float}Reg ops时使用的内容。
	for _, v := range f.Entry.Values {
		if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
			aux := v.Aux.(*AuxNameOffset)
			sl := LocalSlot{N: aux.Name, Type: v.Type, Off: aux.Offset}
			// 在查找表中安装插槽
			idx, _ := sc.lookup(sl)
			// 如果尚未存在，则添加到f.NamedValue
			addToNV(v, sc.canonSlot(idx))
		} else if v.Op.IsCall() {
			// 如果我们打电话，那就太过分了。
			break
		}
	}

	// 现在通过params中的ABI，寻找params
	// 或者我们在上面的循环中没有遇到的一些参数。
	for _, inp := range pri.InParams() {
		if !isNamedRegParam(inp) {
			continue
		}
		n := inp.Name.(*ir.Name)

		// Param分布在一个或多个寄存器中。走过
		// 每件作品，看看我们是否看到它的arg注册操作。
		types, offsets := inp.RegisterTypesAndOffsets()
		for k, t := range types {
			// 注意：此创建LocalSlot的方法是专门设计的
			// 与expand_calls.go中使用的兼容
			// 与分解相反，去吧。expand只调用代码
			// 获取基础名称并在其中创建偏移，
			// 不使用SplitOf/SplitOffset字段。代码
			// 在decompose.go中，情况正好相反——它创建了一个
			// “Off”设置为零的LocalSlot对象，但
			// 指向父插槽的拆分和拆分偏移
			// 将偏移保持在父对象中。
			pieceSlot := LocalSlot{N: n, Type: t, Off: offsets[k]}

			// 看看这篇文章，看看我们有没有看到一个注册操作
			// 为了它。如果没有，创建一个。
			_, found := sc.lookup(pieceSlot)
			if !found {
				// 此插槽没有出现在地图中，这意味着
				// 对应于非活动的in-param，或
				// in参数中未激活/未使用的部分。
				// 为它添加一个新的伪OpArg{Int，Float}Reg。
				synthesizeOpIntFloatArg(n, t, inp.Registers[k],
					pieceSlot)
			}
		}
	}

	// 将新值插入块的头部。
	f.Entry.Values = append(newValues, f.Entry.Values...)
}

// BuildFuncDebug返回f的调试信息。
// f必须被完全处理，这样每个值都会在
// 发出机器代码。
func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32) *FuncDebug {
	if f.RegAlloc == nil {
		f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f)
	}
	state := &f.Cache.debugState
	state.loggingEnabled = loggingEnabled
	state.f = f
	state.registers = f.Config.registers
	state.stackOffset = stackOffset
	state.ctxt = ctxt

	if buildcfg.Experiment.RegabiArgs {
		PopulateABIInRegArgOps(f)
	}

	if state.loggingEnabled {
		state.logf("Generating location lists for function %q\n", f.Name)
	}

	if state.varParts == nil {
		state.varParts = make(map[*ir.Name][]SlotID)
	} else {
		for n := range state.varParts {
			delete(state.varParts, n)
		}
	}

	// 重新组合任何分解的变量，并建立规范
	// 通过填写state.vars和state.slots为每个var和slot创建ID。

	state.slots = state.slots[:0]
	state.vars = state.vars[:0]
	for i, slot := range f.Names {
		state.slots = append(state.slots, *slot)
		if ir.IsSynthetic(slot.N) {
			continue
		}

		topSlot := slot
		for topSlot.SplitOf != nil {
			topSlot = topSlot.SplitOf
		}
		if _, ok := state.varParts[topSlot.N]; !ok {
			state.vars = append(state.vars, topSlot.N)
		}
		state.varParts[topSlot.N] = append(state.varParts[topSlot.N], SlotID(i))
	}

	// 为每个仅堆栈变量重新创建LocalSlot。
	// 作为stackframe的输出，这可能会更好。
	for _, b := range f.Blocks {
		for _, v := range b.Values {
			if v.Op == OpVarDef || v.Op == OpVarKill {
				n := v.Aux.(*ir.Name)
				if ir.IsSynthetic(n) {
					continue
				}

				if _, ok := state.varParts[n]; !ok {
					slot := LocalSlot{N: n, Type: v.Type, Off: 0}
					state.slots = append(state.slots, slot)
					state.varParts[n] = []SlotID{SlotID(len(state.slots) - 1)}
					state.vars = append(state.vars, n)
				}
			}
		}
	}

	// 填写var<->插槽映射。
	if cap(state.varSlots) < len(state.vars) {
		state.varSlots = make([][]SlotID, len(state.vars))
	} else {
		state.varSlots = state.varSlots[:len(state.vars)]
		for i := range state.varSlots {
			state.varSlots[i] = state.varSlots[i][:0]
		}
	}
	if cap(state.slotVars) < len(state.slots) {
		state.slotVars = make([]VarID, len(state.slots))
	} else {
		state.slotVars = state.slotVars[:len(state.slots)]
	}

	if state.partsByVarOffset == nil {
		state.partsByVarOffset = &partsByVarOffset{}
	}
	for varID, n := range state.vars {
		parts := state.varParts[n]
		state.varSlots[varID] = parts
		for _, slotID := range parts {
			state.slotVars[slotID] = VarID(varID)
		}
		*state.partsByVarOffset.(*partsByVarOffset) = partsByVarOffset{parts, state.slots}
		sort.Sort(state.partsByVarOffset)
	}

	state.initializeCache(f, len(state.varParts), len(state.slots))

	for i, slot := range f.Names {
		if ir.IsSynthetic(slot.N) {
			continue
		}
		for _, value := range f.NamedValues[*slot] {
			state.valueNames[value.ID] = append(state.valueNames[value.ID], SlotID(i))
		}
	}

	blockLocs := state.liveness()
	state.buildLocationLists(blockLocs)

	return &FuncDebug{
		Slots:         state.slots,
		VarSlots:      state.varSlots,
		Vars:          state.vars,
		LocationLists: state.lists,
	}
}

// liveness以控制流顺序遍历函数，计算起始值
// 和每个块的结束状态。
func (state *debugState) liveness() []*BlockDebug {
	blockLocs := make([]*BlockDebug, state.f.NumBlocks())

	// 反向后序：尽可能多地访问一个块
	// 访问了前人。
	po := state.f.Postorder()
	for i := len(po) - 1; i >= 0; i-- {
		b := po[i]

		// 从最终生成块的开始状态
		// 它的前身的状态。
		startState, startValid := state.mergePredecessors(b, blockLocs, nil)
		changed := false
		if state.loggingEnabled {
			state.logf("Processing %v, initial state:\n%v", b, state.stateString(state.currentState))
		}

		// 使用每个值的效果更新LOC/寄存器。
		for _, v := range b.Values {
			slots := state.valueNames[v.ID]

			// 加载和存储将继承其源的名称。
			var source *Value
			switch v.Op {
			case OpStoreReg:
				source = v.Args[0]
			case OpLoadReg:
				switch a := v.Args[0]; a.Op {
				case OpArg, OpPhi:
					source = a
				case OpStoreReg:
					source = a.Args[0]
				default:
					if state.loggingEnabled {
						state.logf("at %v: load with unexpected source op: %v (%v)\n", v, a.Op, a)
					}
				}
			}
			// 使用源更新valueNames，以便以后的步骤
			// 不需要特别处理。
			if source != nil {
				slots = append(slots, state.valueNames[source.ID]...)
				state.valueNames[v.ID] = slots
			}

			reg, _ := state.f.getHome(v.ID).(*Register)
			c := state.processValue(v, slots, reg)
			changed = changed || c
		}

		if state.loggingEnabled {
			state.f.Logf("Block %v done, locs:\n%v", b, state.stateString(state.currentState))
		}

		locs := state.allocBlock(b)
		locs.relevant = changed
		if !changed && startValid {
			locs.endState = startState
		} else {
			for slotID, slotLoc := range state.currentState.slots {
				if slotLoc.absent() {
					continue
				}
				state.appendLiveSlot(liveSlot{slot: SlotID(slotID), Registers: slotLoc.Registers, StackOffset: slotLoc.StackOffset})
			}
			locs.endState = state.getLiveSlotSlice()
		}
		blockLocs[b.ID] = locs
	}
	return blockLocs
}

// mergeprecedures获取每个b的前辈的结束状态，并
// 与它们相交以形成b的起始状态。它使该州陷入困境
// blockLocs，并用它填充state.currentState。如果方便的话，它会回来
// 一个重用的[]liveSlot，表示开始状态的true。
// 如果previousBlock为非nil，则它会注册相对于该块末尾的更改
// state.changedVars中的state。请注意，previousBlock通常不是
// 前任
func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug, previousBlock *Block) ([]liveSlot, bool) {
	// 过滤掉后面的树枝。
	var predsBuf [10]*Block
	preds := predsBuf[:0]
	for _, pred := range b.Preds {
		if blockLocs[pred.b.ID] != nil {
			preds = append(preds, pred.b)
		}
	}

	if state.loggingEnabled {
		// 下面的logf将导致pred被堆分配，如果
		// 它是直接通过的。
		preds2 := make([]*Block, len(preds))
		copy(preds2, preds)
		state.logf("Merging %v into %v\n", preds2, b)
	}

	// TODO所有的呼吁都是过分的；只需要对合并中不存在的插槽执行此操作。
	markChangedVars := func(slots []liveSlot) {
		for _, live := range slots {
			state.changedVars.add(ID(state.slotVars[live.slot]))
		}
	}

	if len(preds) == 0 {
		if previousBlock != nil {
			// 将上一个块中的所有内容标记为已更改，因为它不是前一个块。
			markChangedVars(blockLocs[previousBlock.ID].endState)
		}
		state.currentState.reset(nil)
		return nil, true
	}

	p0 := blockLocs[preds[0].ID].endState
	if len(preds) == 1 {
		if previousBlock != nil && preds[0].ID != previousBlock.ID {
			// 将上一个块中的所有内容标记为已更改，因为它不是前一个块。
			markChangedVars(blockLocs[previousBlock.ID].endState)
		}
		state.currentState.reset(p0)
		return p0, true
	}

	baseID := preds[0].ID
	baseState := p0

	// 如果上一个块不是前置块，则其位置信息将在与此块的边界处更改。
	previousBlockIsNotPredecessor := previousBlock != nil // 如果为零，则无需更改信息。

	if previousBlock != nil {
		// 尝试使用previousBlock作为基本状态
		// 如果可能的话。
		for _, pred := range preds[1:] {
			if pred.ID == previousBlock.ID {
				baseID = pred.ID
				baseState = blockLocs[pred.ID].endState
				previousBlockIsNotPredecessor = false
				break
			}
		}
	}

	if state.loggingEnabled {
		state.logf("Starting %v with state from b%v:\n%v", b, baseID, state.blockEndStateString(blockLocs[baseID]))
	}

	slotLocs := state.currentState.slots
	for _, predSlot := range baseState {
		slotLocs[predSlot.slot] = VarLoc{predSlot.Registers, predSlot.StackOffset}
		state.liveCount[predSlot.slot] = 1
	}
	for _, pred := range preds {
		if pred.ID == baseID {
			continue
		}
		if state.loggingEnabled {
			state.logf("Merging in state from %v:\n%v", pred, state.blockEndStateString(blockLocs[pred.ID]))
		}
		for _, predSlot := range blockLocs[pred.ID].endState {
			state.liveCount[predSlot.slot]++
			liveLoc := slotLocs[predSlot.slot]
			if !liveLoc.onStack() || !predSlot.onStack() || liveLoc.StackOffset != predSlot.StackOffset {
				liveLoc.StackOffset = 0
			}
			liveLoc.Registers &= predSlot.Registers
			slotLocs[predSlot.slot] = liveLoc
		}
	}

	// 检查最终状态是否与第一个前置状态相同
	// 最终状态，并重用它（如果是）。原则上它可以匹配任何，
	// 但它可能比第一个更不值得检查。
	unchanged := true
	for _, predSlot := range baseState {
		if state.liveCount[predSlot.slot] != len(preds) ||
			slotLocs[predSlot.slot].Registers != predSlot.Registers ||
			slotLocs[predSlot.slot].StackOffset != predSlot.StackOffset {
			unchanged = false
			break
		}
	}
	if unchanged {
		if state.loggingEnabled {
			state.logf("After merge, %v matches b%v exactly.\n", b, baseID)
		}
		if previousBlockIsNotPredecessor {
			// 将上一个块中的所有内容标记为已更改，因为它不是前一个块。
			markChangedVars(blockLocs[previousBlock.ID].endState)
		}
		state.currentState.reset(baseState)
		return baseState, true
	}

	for reg := range state.currentState.registers {
		state.currentState.registers[reg] = state.currentState.registers[reg][:0]
	}

	// 一个插槽是活的，如果它在所有的前辈中都出现过，那么他们都有
	// 一些共同的存储。
	for _, predSlot := range baseState {
		slotLoc := slotLocs[predSlot.slot]

		if state.liveCount[predSlot.slot] != len(preds) {
			// 只在一些前辈中见过。把它弄清楚。
			slotLocs[predSlot.slot] = VarLoc{}
			continue
		}

		// 存在于所有的前辈中。
		mask := uint64(slotLoc.Registers)
		for {
			if mask == 0 {
				break
			}
			reg := uint8(bits.TrailingZeros64(mask))
			mask &^= 1 << reg
			state.currentState.registers[reg] = append(state.currentState.registers[reg], predSlot.slot)
		}
	}

	if previousBlockIsNotPredecessor {
		// 将上一个块中的所有内容标记为已更改，因为它不是前一个块。
		markChangedVars(blockLocs[previousBlock.ID].endState)

	}
	return nil, false
}

// processValue更新LOC和state.RegisterContent以反映v，这是一个具有
// 在vSlots中的名称和在vReg中的主页。“v”在执行后变为可见
// 这是评估它的说明。它返回被修改的变量
// 价值观的执行。
func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) bool {
	locs := state.currentState
	changed := false
	setSlot := func(slot SlotID, loc VarLoc) {
		changed = true
		state.changedVars.add(ID(state.slotVars[slot]))
		state.currentState.slots[slot] = loc
	}

	// 处理任何寄存器碰撞。例如，调用操作，
	// 关闭所有寄存器，即使它们没有显式写入
	// 他们
	clobbers := uint64(opcodeTable[v.Op].reg.clobbers)
	for {
		if clobbers == 0 {
			break
		}
		reg := uint8(bits.TrailingZeros64(clobbers))
		clobbers &^= 1 << reg

		for _, slot := range locs.registers[reg] {
			if state.loggingEnabled {
				state.logf("at %v: %v clobbered out of %v\n", v, state.slots[slot], &state.registers[reg])
			}

			last := locs.slots[slot]
			if last.absent() {
				state.f.Fatalf("at %v: slot %v in register %v with no location entry", v, state.slots[slot], &state.registers[reg])
				continue
			}
			regs := last.Registers &^ (1 << reg)
			setSlot(slot, VarLoc{regs, last.StackOffset})
		}

		locs.registers[reg] = locs.registers[reg][:0]
	}

	switch {
	case v.Op == OpVarDef, v.Op == OpVarKill:
		n := v.Aux.(*ir.Name)
		if ir.IsSynthetic(n) {
			break
		}

		slotID := state.varParts[n][0]
		var stackOffset StackOffset
		if v.Op == OpVarDef {
			stackOffset = StackOffset(state.stackOffset(state.slots[slotID])<<1 | 1)
		}
		setSlot(slotID, VarLoc{0, stackOffset})
		if state.loggingEnabled {
			if v.Op == OpVarDef {
				state.logf("at %v: stack-only var %v now live\n", v, state.slots[slotID])
			} else {
				state.logf("at %v: stack-only var %v now dead\n", v, state.slots[slotID])
			}
		}

	case v.Op == OpArg:
		home := state.f.getHome(v.ID).(LocalSlot)
		stackOffset := state.stackOffset(home)<<1 | 1
		for _, slot := range vSlots {
			if state.loggingEnabled {
				state.logf("at %v: arg %v now on stack in location %v\n", v, state.slots[slot], home)
				if last := locs.slots[slot]; !last.absent() {
					state.logf("at %v: unexpected arg op on already-live slot %v\n", v, state.slots[slot])
				}
			}

			setSlot(slot, VarLoc{0, StackOffset(stackOffset)})
		}

	case v.Op == OpStoreReg:
		home := state.f.getHome(v.ID).(LocalSlot)
		stackOffset := state.stackOffset(home)<<1 | 1
		for _, slot := range vSlots {
			last := locs.slots[slot]
			if last.absent() {
				if state.loggingEnabled {
					state.logf("at %v: unexpected spill of unnamed register %s\n", v, vReg)
				}
				break
			}

			setSlot(slot, VarLoc{last.Registers, StackOffset(stackOffset)})
			if state.loggingEnabled {
				state.logf("at %v: %v spilled to stack location %v\n", v, state.slots[slot], home)
			}
		}

	case vReg != nil:
		if state.loggingEnabled {
			newSlots := make([]bool, len(state.slots))
			for _, slot := range vSlots {
				newSlots[slot] = true
			}

			for _, slot := range locs.registers[vReg.num] {
				if !newSlots[slot] {
					state.logf("at %v: overwrote %v in register %v\n", v, state.slots[slot], vReg)
				}
			}
		}

		for _, slot := range locs.registers[vReg.num] {
			last := locs.slots[slot]
			setSlot(slot, VarLoc{last.Registers &^ (1 << uint8(vReg.num)), last.StackOffset})
		}
		locs.registers[vReg.num] = locs.registers[vReg.num][:0]
		locs.registers[vReg.num] = append(locs.registers[vReg.num], vSlots...)
		for _, slot := range vSlots {
			if state.loggingEnabled {
				state.logf("at %v: %v now in %s\n", v, state.slots[slot], vReg)
			}

			last := locs.slots[slot]
			setSlot(slot, VarLoc{1<<uint8(vReg.num) | last.Registers, last.StackOffset})
		}
	}
	return changed
}

// varOffset返回它所使用的用户变量中插槽的偏移量
// 分解自。这与其堆栈偏移量无关。
func varOffset(slot LocalSlot) int64 {
	offset := slot.Off
	s := &slot
	for ; s.SplitOf != nil; s = s.SplitOf {
		offset += s.SplitOffset
	}
	return offset
}

type partsByVarOffset struct {
	slotIDs []SlotID
	slots   []LocalSlot
}

func (a partsByVarOffset) Len() int { return len(a.slotIDs) }
func (a partsByVarOffset) Less(i, j int) bool {
	return varOffset(a.slots[a.slotIDs[i]]) < varOffset(a.slots[a.slotIDs[j]])
}
func (a partsByVarOffset) Swap(i, j int) { a.slotIDs[i], a.slotIDs[j] = a.slotIDs[j], a.slotIDs[i] }

// pendingEntry表示位置列表项的开头，缺少
// 只有它的端点坐标。
type pendingEntry struct {
	present                bool
	startBlock, startValue ID
	// 每个变量的位置，顺序与
	// 斯洛蒂德在内翻部分。
	pieces []VarLoc
}

func (e *pendingEntry) clear() {
	e.present = false
	e.startBlock = 0
	e.startValue = 0
	for i := range e.pieces {
		e.pieces[i] = VarLoc{}
	}
}

// canMerge报告new的位置描述是否与相同
// 悬而未决的
func canMerge(pending, new VarLoc) bool {
	if pending.absent() && new.absent() {
		return true
	}
	if pending.absent() || new.absent() {
		return false
	}
	if pending.onStack() {
		return pending.StackOffset == new.StackOffset
	}
	if pending.Registers != 0 && new.Registers != 0 {
		return firstReg(pending.Registers) == firstReg(new.Registers)
	}
	return false
}

// firstReg返回集合中存在的第一个寄存器。
func firstReg(set RegisterSet) uint8 {
	if set == 0 {
		// 这是错误的，但似乎有些情况下我们
		// 生产没有存储的位置。
		return 0
	}
	return uint8(bits.TrailingZeros64(uint64(set)))
}

// BuildLocationList为中的所有用户变量构建位置列表
// state.f，使用blockLocs中有关块状态的信息。
// 返回的位置列表不完整。他们是在
// SSA值而不是PCs，并且没有基址/结束项。他们将
// 由PutLocationList完成。
func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
	// 按程序文本顺序运行函数，建立位置
	// 清单，我们去。繁重的工作基本上已经完成了。

	var prevBlock *Block
	for _, b := range state.f.Blocks {
		state.mergePredecessors(b, blockLocs, prevBlock)

		if !blockLocs[b.ID].relevant {
			// 处理前置块和上一个块（可能不是前置块）之间的任何差异
			for _, varID := range state.changedVars.contents() {
				state.updateVar(VarID(varID), b, BlockStart)
			}
			continue
		}

		mustBeFirst := func(v *Value) bool {
			return v.Op == OpPhi || v.Op.isLoweredGetClosurePtr() ||
				v.Op == OpArgIntReg || v.Op == OpArgFloatReg
		}

		zeroWidthPending := false
		blockPrologComplete := false // 在第一个非零宽度op时设置为true
		apcChangedSize := 0          // 前导arg、Phi、ClosurePtr的changedVars大小
		// 期望在模式中看到值（apc）*（零宽度|实值）*
		for _, v := range b.Values {
			slots := state.valueNames[v.ID]
			reg, _ := state.f.getHome(v.ID).(*Register)
			changed := state.processValue(v, slots, reg) // changed==添加到state.changedVars

			if opcodeTable[v.Op].zeroWidth {
				if changed {
					if mustBeFirst(v) || v.Op == OpArg {
						// 这些范围从块的真正开始处开始，而不是在第一条指令之后
						if blockPrologComplete && mustBeFirst(v) {
							panic(fmt.Errorf("Unexpected placement of op '%s' appearing after non-pseudo-op at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func))
						}
						apcChangedSize = len(state.changedVars.contents())
						// 其他零宽度操作必须等待“真实”操作。
						zeroWidthPending = true
						continue
					}
				}
				continue
			}

			if !changed && !zeroWidthPending {
				continue
			}
			// 宽度不为零；i、 例如，“真正的”指令。

			zeroWidthPending = false
			blockPrologComplete = true
			for i, varID := range state.changedVars.contents() {
				if i < apcChangedSize { // 缓冲块更改的真正开始
					state.updateVar(VarID(varID), v.Block, BlockStart)
				} else {
					state.updateVar(VarID(varID), v.Block, v)
				}
			}
			state.changedVars.clear()
			apcChangedSize = 0
		}
		for i, varID := range state.changedVars.contents() {
			if i < apcChangedSize { // 缓冲块更改的真正开始
				state.updateVar(VarID(varID), b, BlockStart)
			} else {
				state.updateVar(VarID(varID), b, BlockEnd)
			}
		}

		prevBlock = b
	}

	if state.loggingEnabled {
		state.logf("location lists:\n")
	}

	// 刷新最后一个块末尾的所有剩余条目。
	for varID := range state.lists {
		state.writePendingEntry(VarID(varID), state.f.Blocks[len(state.f.Blocks)-1].ID, FuncEnd.ID)
		list := state.lists[varID]
		if state.loggingEnabled {
			if len(list) == 0 {
				state.logf("\t%v : empty list\n", state.vars[varID])
			} else {
				state.logf("\t%v : %q\n", state.vars[varID], hex.EncodeToString(state.lists[varID]))
			}
		}
	}
}

// updateVar将varID的挂起位置列表项更新为
// 反映CULLOC中的新位置，从b区的v开始。
// v可以是指示块开始或结束的特殊值之一。
func (state *debugState) updateVar(varID VarID, b *Block, v *Value) {
	curLoc := state.currentState.slots
	// 将位置列表项与任何活动项组合在一起。
	empty := true
	for _, slotID := range state.varSlots[varID] {
		if !curLoc[slotID].absent() {
			empty = false
			break
		}
	}
	pending := &state.pendingEntries[varID]
	if empty {
		state.writePendingEntry(varID, b.ID, v.ID)
		pending.clear()
		return
	}

	// 如果可能，扩展上一个条目。
	if pending.present {
		merge := true
		for i, slotID := range state.varSlots[varID] {
			if !canMerge(pending.pieces[i], curLoc[slotID]) {
				merge = false
				break
			}
		}
		if merge {
			return
		}
	}

	state.writePendingEntry(varID, b.ID, v.ID)
	pending.present = true
	pending.startBlock = b.ID
	pending.startValue = v.ID
	for i, slot := range state.varSlots[varID] {
		pending.pieces[i] = curLoc[slot]
	}
}

// writePendingEntry写出varID的挂起条目（如果有），
// 终止于endBlock/Value。
func (state *debugState) writePendingEntry(varID VarID, endBlock, endValue ID) {
	pending := state.pendingEntries[varID]
	if !pending.present {
		return
	}

	// 将开始/结束坐标打包到开始/结束地址
	// 条目的，用于通过PutLocationList进行解码。
	start, startOK := encodeValue(state.ctxt, pending.startBlock, pending.startValue)
	end, endOK := encodeValue(state.ctxt, endBlock, endValue)
	if !startOK || !endOK {
		// 如果有人编写的函数使用>65K的值，
		// 他们在32位平台上获得不完整的调试信息。
		return
	}
	if start == end {
		if state.loggingEnabled {
			// Printf不是logf，因此不受GOSSAFUNC的限制；这应该很少开火。
			fmt.Printf("Skipping empty location list for %v in %s\n", state.vars[varID], state.f.Name)
		}
		return
	}

	list := state.lists[varID]
	list = appendPtr(state.ctxt, list, start)
	list = appendPtr(state.ctxt, list, end)
	// 在哪里写一次位置描述的长度
	// 我们知道它有多大。
	sizeIdx := len(list)
	list = list[:len(list)+2]

	if state.loggingEnabled {
		var partStrs []string
		for i, slot := range state.varSlots[varID] {
			partStrs = append(partStrs, fmt.Sprintf("%v@%v", state.slots[slot], state.LocString(pending.pieces[i])))
		}
		state.logf("Add entry for %v: \tb%vv%v-b%vv%v = \t%v\n", state.vars[varID], pending.startBlock, pending.startValue, endBlock, endValue, strings.Join(partStrs, " "))
	}

	for i, slotID := range state.varSlots[varID] {
		loc := pending.pieces[i]
		slot := state.slots[slotID]

		if !loc.absent() {
			if loc.onStack() {
				if loc.stackOffsetValue() == 0 {
					list = append(list, dwarf.DW_OP_call_frame_cfa)
				} else {
					list = append(list, dwarf.DW_OP_fbreg)
					list = dwarf.AppendSleb128(list, int64(loc.stackOffsetValue()))
				}
			} else {
				regnum := state.ctxt.Arch.DWARFRegisters[state.registers[firstReg(loc.Registers)].ObjNum()]
				if regnum < 32 {
					list = append(list, dwarf.DW_OP_reg0+byte(regnum))
				} else {
					list = append(list, dwarf.DW_OP_regx)
					list = dwarf.AppendUleb128(list, uint64(regnum))
				}
			}
		}

		if len(state.varSlots[varID]) > 1 {
			list = append(list, dwarf.DW_OP_piece)
			list = dwarf.AppendUleb128(list, uint64(slot.Type.Size()))
		}
	}
	state.ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
	state.lists[varID] = list
}

// PutLocationList将list（其中间表示形式中的位置列表）添加到listSym。
func (debugInfo *FuncDebug) PutLocationList(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) {
	getPC := debugInfo.GetPC

	if ctxt.UseBASEntries {
		listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, ^0)
		listSym.WriteAddr(ctxt, listSym.Size, ctxt.Arch.PtrSize, startPC, 0)
	}

	// 重新读取列表，将其地址从块/值ID转换为PC。
	for i := 0; i < len(list); {
		begin := getPC(decodeValue(ctxt, readPtr(ctxt, list[i:])))
		end := getPC(decodeValue(ctxt, readPtr(ctxt, list[i+ctxt.Arch.PtrSize:])))

		// 可怕的黑客。如果范围仅包含零宽度
		// 说明，例如Arg，它位于
		// 函数，这与
		// 结束输入。胡扯。
		if begin == 0 && end == 0 {
			end = 1
		}

		if ctxt.UseBASEntries {
			listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, int64(begin))
			listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, int64(end))
		} else {
			listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, int64(begin))
			listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, int64(end))
		}

		i += 2 * ctxt.Arch.PtrSize
		datalen := 2 + int(ctxt.Arch.ByteOrder.Uint16(list[i:]))
		listSym.WriteBytes(ctxt, listSym.Size, list[i:i+datalen]) // 复制数据长度和位置编码
		i += datalen
	}

	// 位置列表内容，现在有真正的PC。
	// 结束输入。
	listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
	listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
}

// 将值和块ID打包到地址大小的uint中，返回编码的
// 值和布尔值，指示编码是否成功。对于
// 32位体系结构对于非常大的过程，该过程可能会失败
// （理论上，在系统中降低调试质量是可以的
// （本案）。
func encodeValue(ctxt *obj.Link, b, v ID) (uint64, bool) {
	if ctxt.Arch.PtrSize == 8 {
		result := uint64(b)<<32 | uint64(uint32(v))
		// ctxt.Logf（“b%#x（%d）v%#x（%d）->%#x\n”，b，b，v，v，result）
		return result, true
	}
	if ctxt.Arch.PtrSize != 4 {
		panic("unexpected pointer size")
	}
	if ID(int16(b)) != b || ID(int16(v)) != v {
		return 0, false
	}
	return uint64(b)<<16 | uint64(uint16(v)), true
}

// 解压缩一个值和由encodeValue编码的块ID。
func decodeValue(ctxt *obj.Link, word uint64) (ID, ID) {
	if ctxt.Arch.PtrSize == 8 {
		b, v := ID(word>>32), ID(word)
		// ctxt.Logf（“%#x->b%#x（%d）v%#x（%d）\n”，单词，b，b，v，v）
		return b, v
	}
	if ctxt.Arch.PtrSize != 4 {
		panic("unexpected pointer size")
	}
	return ID(word >> 16), ID(int16(word))
}

// 将指针大小的uint追加到buf。
func appendPtr(ctxt *obj.Link, buf []byte, word uint64) []byte {
	if cap(buf) < len(buf)+20 {
		b := make([]byte, len(buf), 20+cap(buf)*2)
		copy(b, buf)
		buf = b
	}
	writeAt := len(buf)
	buf = buf[0 : len(buf)+ctxt.Arch.PtrSize]
	writePtr(ctxt, buf[writeAt:], word)
	return buf
}

// 将指针大小的uint写入buf的开头。
func writePtr(ctxt *obj.Link, buf []byte, word uint64) {
	switch ctxt.Arch.PtrSize {
	case 4:
		ctxt.Arch.ByteOrder.PutUint32(buf, uint32(word))
	case 8:
		ctxt.Arch.ByteOrder.PutUint64(buf, word)
	default:
		panic("unexpected pointer size")
	}

}

// 从buf开头读取指针大小的uint。
func readPtr(ctxt *obj.Link, buf []byte) uint64 {
	switch ctxt.Arch.PtrSize {
	case 4:
		return uint64(ctxt.Arch.ByteOrder.Uint32(buf))
	case 8:
		return ctxt.Arch.ByteOrder.Uint64(buf)
	default:
		panic("unexpected pointer size")
	}

}

// SetupLocalList为一个项目创建位置列表的初始部分
// 用户变量。它发出编码的范围开始/结束和
// 大小的占位符。返回值是新列表加上
// 列表中保存大小的插槽（稍后更新）。
func setupLocList(ctxt *obj.Link, f *Func, list []byte, st, en ID) ([]byte, int) {
	start, startOK := encodeValue(ctxt, f.Entry.ID, st)
	end, endOK := encodeValue(ctxt, f.Entry.ID, en)
	if !startOK || !endOK {
		// 如果有人编写的函数使用
		// >32位平台上的65K值。希望是一个降级的调试
		// 在那种情况下，经验是可以的。
		return nil, 0
	}
	list = appendPtr(ctxt, list, start)
	list = appendPtr(ctxt, list, end)

	// 在哪里写一次位置描述的长度
	// 我们知道它有多大。
	sizeIdx := len(list)
	list = list[:len(list)+2]
	return list, sizeIdx
}

// locatePrologEnd使用传入函数遍历函数的入口块
// 注册参数并查找prolog中的最后一条指令
// 这溢出了一个寄存器arg。它返回该指令的ID
// 例子：
// None
// b1：
// v3=ArgIntReg<int>{p1+0}[0]：AX
// ... 更多arg regs。。
// v4=ArgFloatReg<float32>{f1+0}[0]：X0
// v52=MOVQstore<mem>{p1}v2 v3 v1
// ... 更多的商店。。。
// v68=movsstore<mem>{f4}v2 v67 v66
// v38=MOVQstoreconst<mem>{blob}[val=0，off=0]v2 v32
// None
// 重要提示：LocatePrologen仅在以下情况下才能正常工作：
// 优化关闭（例如“-N”）。如果启用了优化
// 我们无法确定是否能找到所有溢出到数据库中的输入参数
// 入口块序言。
func locatePrologEnd(f *Func) ID {

	// 如果此指令看起来像是移动了ABI，则返回true
	// 与存储的值一起注册到堆栈。
	isRegMoveLike := func(v *Value) (bool, ID) {
		n, ok := v.Aux.(*ir.Name)
		var r ID
		if !ok || n.Class != ir.PPARAM {
			return false, r
		}
		regInputs, memInputs, spInputs := 0, 0, 0
		for _, a := range v.Args {
			if a.Op == OpArgIntReg || a.Op == OpArgFloatReg {
				regInputs++
				r = a.ID
			} else if a.Type.IsMemory() {
				memInputs++
			} else if a.Op == OpSP {
				spInputs++
			} else {
				return false, r
			}
		}
		return v.Type.IsMemory() && memInputs == 1 &&
			regInputs == 1 && spInputs == 1, r
	}

	// 到目前为止，我们在向前行走时看到的OpArg*Reg值，
	// 我们还没有看到相应的泄漏。
	regArgs := make([]ID, 0, 32)

	// removeReg尝试从regArgs中删除一个值，返回true
	// 如果发现并删除，则为假，否则为假。
	removeReg := func(r ID) bool {
		for i := 0; i < len(regArgs); i++ {
			if regArgs[i] == r {
				regArgs = append(regArgs[:i], regArgs[i+1:]...)
				return true
			}
		}
		return false
	}

	// 穿过街区向前走。当我们看到OpArg*Reg时，记录
	// 它在regArgs列表中生成的值。当你看到一家使用
	// 值，删除该条目。当我们到达最后一家商店时（使用）
	// 那么我们已经到了序言的末尾。
	for k, v := range f.Entry.Values {
		if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
			regArgs = append(regArgs, v.ID)
			continue
		}
		if ok, r := isRegMoveLike(v); ok {
			if removed := removeReg(r); removed {
				if len(regArgs) == 0 {
					// 发现我们最后一次泄漏；返回后面的值
					// 信息技术请注意，这可能是泄漏
					// 块中的最后一条指令。如果是，那么
					// 返回“阻塞结束”哨兵。
					if k < len(f.Entry.Values)-1 {
						return f.Entry.Values[k+1].ID
					}
					return BlockEnd.ID
				}
			}
		}
		if v.Op.IsCall() {
			// 如果我们打电话，那就太过分了。
			return v.ID
		}
	}
	// 什么也没找到
	return ID(-1)
}

// 如果参数对应于“p”，则isNamedRegParam返回true
// 指定给一个或多个的命名的非空输入参数
// 登记册。
func isNamedRegParam(p abi.ABIParamAssignment) bool {
	if p.Name == nil {
		return false
	}
	n := p.Name.(*ir.Name)
	if n.Sym() == nil || n.Sym().IsBlank() {
		return false
	}
	if len(p.Registers) == 0 {
		return false
	}
	return true
}

// BuildFuncDebugNoOptimized使用
// 对应于的寄存器驻留输入参数的条目
// 函数“f”；当我们在编译时不使用它
// 但寄存器ABI已启用。对于每个reg参数，
// 它构造了一个由两个元素组成的位置列表：第一个元素包含
// 输入寄存器，第二个元素保存堆栈位置
// 参数（假设优化关闭时，
// 每个输入参数reg将在序言中溢出。
func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32) *FuncDebug {
	fd := FuncDebug{}

	pri := f.ABISelf.ABIAnalyzeFuncType(f.Type.FuncType())

	// 查看是否有任何已命名的寄存器提升参数。
	// 如果没有，请尽早保释，让打电话的人来处理
	// 对于其余的参数/局部变量，请退出。
	numRegParams := 0
	for _, inp := range pri.InParams() {
		if isNamedRegParam(inp) {
			numRegParams++
		}
	}
	if numRegParams == 0 {
		return &fd
	}

	state := debugState{f: f}

	if loggingEnabled {
		state.logf("generating -N reg param loc lists for func %q\n", f.Name)
	}

	// 分配位置列表。
	fd.LocationLists = make([][]byte, numRegParams)

	// 找到与最后一次泄漏的泄漏相对应的值
	// 输入寄存器。
	afterPrologVal := locatePrologEnd(f)

	// 再次遍历输入参数并处理寄存器驻留元素。
	pidx := 0
	for _, inp := range pri.InParams() {
		if !isNamedRegParam(inp) {
			// 将在其他地方整理
			continue
		}

		n := inp.Name.(*ir.Name)
		sl := LocalSlot{N: n, Type: inp.Type, Off: 0}
		fd.Vars = append(fd.Vars, n)
		fd.Slots = append(fd.Slots, sl)
		slid := len(fd.VarSlots)
		fd.VarSlots = append(fd.VarSlots, []SlotID{SlotID(slid)})

		if afterPrologVal == ID(-1) {
			// 这可能发生在退化函数的无穷大
			// 循环，如45948期中的循环。在这种情况下，请离开
			// 为参数设置的var/slot，但不要尝试
			// 发出一个位置列表。
			if loggingEnabled {
				state.logf("locatePrologEnd failed, skipping %v\n", n)
			}
			pidx++
			continue
		}

		// Param正在一个或多个寄存器中到达。我们需要一个2元素
		// 它的位置表达式。位置列表中的第一个条目
		// 将对应于输入寄存器中的生存期。
		list, sizeIdx := setupLocList(ctxt, f, fd.LocationLists[pidx],
			BlockStart.ID, afterPrologVal)
		if list == nil {
			pidx++
			continue
		}
		if loggingEnabled {
			state.logf("param %v:\n  [<entry>, %d]:\n", n, afterPrologVal)
		}
		rtypes, _ := inp.RegisterTypesAndOffsets()
		padding := make([]uint64, 0, 32)
		padding = inp.ComputePadding(padding)
		for k, r := range inp.Registers {
			reg := ObjRegForAbiReg(r, f.Config)
			dwreg := ctxt.Arch.DWARFRegisters[reg]
			if dwreg < 32 {
				list = append(list, dwarf.DW_OP_reg0+byte(dwreg))
			} else {
				list = append(list, dwarf.DW_OP_regx)
				list = dwarf.AppendUleb128(list, uint64(dwreg))
			}
			if loggingEnabled {
				state.logf("    piece %d -> dwreg %d", k, dwreg)
			}
			if len(inp.Registers) > 1 {
				list = append(list, dwarf.DW_OP_piece)
				ts := rtypes[k].Width
				list = dwarf.AppendUleb128(list, uint64(ts))
				if padding[k] > 0 {
					if loggingEnabled {
						state.logf(" [pad %d bytes]", padding[k])
					}
					list = append(list, dwarf.DW_OP_piece)
					list = dwarf.AppendUleb128(list, padding[k])
				}
			}
			if loggingEnabled {
				state.logf("\n")
			}
		}
		// 填写位置表达式元素的长度
		ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))

		// 位置列表中的第二个条目将是堆栈主目录
		// 一旦它被泼洒了，它就会被释放。现在就发射。
		list, sizeIdx = setupLocList(ctxt, f, list,
			afterPrologVal, FuncEnd.ID)
		if list == nil {
			pidx++
			continue
		}
		soff := stackOffset(sl)
		if soff == 0 {
			list = append(list, dwarf.DW_OP_call_frame_cfa)
		} else {
			list = append(list, dwarf.DW_OP_fbreg)
			list = dwarf.AppendSleb128(list, int64(soff))
		}
		if loggingEnabled {
			state.logf("  [%d, <end>): stackOffset=%d\n", afterPrologVal, soff)
		}

		// 填写尺寸
		ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))

		fd.LocationLists[pidx] = list
		pidx++
	}
	return &fd
}
